package mil.af.amc.adpetracker.service;

import mil.af.amc.adpetracker.dao.AdpeAccountDao;
import mil.af.amc.adpetracker.dao.DraDao;
import mil.af.amc.adpetracker.dao.UserDao;
import mil.af.amc.adpetracker.exceptions.IllegalUnassignmentOfPrimaryCustodianException;
import mil.af.amc.adpetracker.exceptions.UserNotFoundException;
import mil.af.amc.adpetracker.mail.JavaMailSimpleMailSender;
import mil.af.amc.adpetracker.model.AdpeAccount;
import mil.af.amc.adpetracker.model.DRA;
import mil.af.amc.adpetracker.model.EquipmentItem;
import mil.af.amc.adpetracker.model.User;
import mil.af.amc.adpetracker.service.exceptions.AdpeAccountNotFoundException;
import mil.af.amc.adpetracker.service.exceptions.DraNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * Implementation of AdpeAccountService
 *
 * @author TSgt Jason Ferguson
 */

@Service("adpeAccountService")
public class AdpeAccountServiceImpl implements AdpeAccountService {

    private AdpeAccountDao adpeAccountDao;

    @Autowired
    public void setAdpeAccountDao(AdpeAccountDao adpeAccountDao) {
        this.adpeAccountDao = adpeAccountDao;
    }

    private DraDao draDao;

    @Autowired
    public void setDraDao(DraDao draDao) {
        this.draDao = draDao;
    }

    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public AdpeAccount getById(int accountId) {
        return adpeAccountDao.getById(accountId);
    }

    public List<AdpeAccount> getAll() {
        return adpeAccountDao.getAll();
    }

    public List<EquipmentItem> getEquipmentItemList() {
        throw new UnsupportedOperationException();
    }

    public List<EquipmentItem> getPartialEquipmentItemList(int startPage, int total) {
        throw new UnsupportedOperationException();
    }

    public List<User> getCustodians() {
        throw new UnsupportedOperationException();
    }

    public int getEquipmentItemCount() {

        return adpeAccountDao.getCount();
    }

    public int getEquipmentItemCount(int adpeAccountId) {

        AdpeAccount acct = adpeAccountDao.getById(adpeAccountId);
        return getEquipmentItemCount(acct);

    }
    public int getEquipmentItemCount(AdpeAccount account) {

        return adpeAccountDao.getEquipmentItemCount(account);
    }

    public double getAccountValue(int adpeAccountId) {

        AdpeAccount acct = adpeAccountDao.getById(adpeAccountId);
        return getAccountValue(acct);

    }

    public double getAccountValue(AdpeAccount account) {

        return adpeAccountDao.getAccountValue(account);

    }

    public AdpeAccount createAccount(Map acctMap) throws DraNotFoundException {

        String accountName = (String) acctMap.get("accountName");
        int draId = (Integer) acctMap.get("draId");

        DRA dra = draDao.getById(draId);
        if (dra == null) {
            throw new DraNotFoundException("DRA#" + draId + " not found.");
        }

        AdpeAccount acct = new AdpeAccount(accountName, dra);


        // TODO: Set the account custodians

        int accountId = adpeAccountDao.add(acct);
        acct.setId(accountId);

        return acct;

    }

    public AdpeAccount createAccount(DRA dra, String accountName) throws DraNotFoundException {

        AdpeAccount acct = new AdpeAccount(accountName, dra);

        int accountId = adpeAccountDao.add(acct);
        acct.setId(accountId);

        return acct;
    }

    public AdpeAccount createAccount(int draId, String accountName) throws DraNotFoundException {

        DRA dra = draDao.getById(draId);
        if (dra == null) {
            throw new DraNotFoundException("DRA#" + draId + " not found.");
        }

        AdpeAccount acct = new AdpeAccount(accountName, dra);
        int accountId = adpeAccountDao.add(acct);
        acct.setId(accountId);

        return acct;
    }

    public void deleteAccount(int adpeAccountId) throws ServiceException {

        AdpeAccount acct = adpeAccountDao.getById(adpeAccountId);
        deleteAccount(acct);
    }

    public void deleteAccount(AdpeAccount acct) throws ServiceException {

        if (acct.getAccountInventory().size()==0 || acct.getAccountInventory()==null) {
        adpeAccountDao.delete(acct);
        } else {
            throw new ServiceException("messages.adpeaccount.assignedequipment",
                    "Cannot delete ADPE account because it still has assigned equipment");
            //throw new AdpeAccountHasAssignedEquipmentException();
        }
    }

    public void assignPrimaryCustodian(Map custodianMap) throws ServiceException {

        Integer accountId = (Integer) custodianMap.get("adpeAccountId");
        if (accountId == null) {
            throw new ServiceException("messages.adpeaccount.invalid.accountid", "Invalid ADPE Account id#" + accountId);
        }

        Integer userId = (Integer) custodianMap.get("userId");
        if (userId == null) {
            throw new ServiceException("messages.adpeaccount.invalid.userid", "Invalid User id#" + userId);
        }

        AdpeAccount acct = adpeAccountDao.getById(accountId);
        User user = userDao.getById(userId);

        // The same person can't be both a primary and an alternate custodian
        if (acct.getAlternateCustodians().contains(user)) {
            throw new ServiceException("messages.adpeaccount.duplicatecustodianassignment", "Attempt to assign a User as both a Primary and Alternate custodian");
        }

        acct.getPrimaryCustodian().clear();
        acct.getPrimaryCustodian().add(user);
        //acct.getPrimaryCustodian().set(0, user);

        adpeAccountDao.saveOrUpdate(acct);

    }

    public void assignAlternateCustodian(int adpeAccountId, Integer[] alternateCustodianUserId) throws ServiceException, IllegalUnassignmentOfPrimaryCustodianException {

        // ensure that a valid AdpeAccount object is returned
        if (adpeAccountId == 0) {
            throw new ServiceException("messages.adpecustodian.invalidaccountid","Invalid account id");
        }

        AdpeAccount acct = adpeAccountDao.getById(adpeAccountId);
        if (acct == null) {
            throw new ServiceException("messages.adpecustodian.invalidaccountid","Invalid account id");
        }

        for (int i: alternateCustodianUserId) {

            User user = userDao.getById(i);

            if (acct.getPrimaryCustodian().contains(user)) {
                throw new IllegalUnassignmentOfPrimaryCustodianException();
            }

            acct.getAlternateCustodians().add(user);
            adpeAccountDao.saveOrUpdate(acct);

            StringBuilder sb = new StringBuilder();
            sb.append("You have been assigned as Alternate equipment custodian for ADPE Account ");
            sb.append(acct.getName());
            sb.append(" . If this is in error, please contact the Primary Account Custodian \r\n");
            new JavaMailSimpleMailSender().sendMessage(user,"Topic Assignment As Equipment Custodian", sb.toString());
        }


    }

    @SuppressWarnings({"ForLoopReplaceableByForEach"})
    @Deprecated
    public void assignAlternateCustodian(Map custodianMap) throws ServiceException, IllegalUnassignmentOfPrimaryCustodianException {

        // get the account id and make sure it's valid
        Integer accountId = (Integer) custodianMap.get("adpeAccountId");
        if (accountId == null) {
            throw new ServiceException("messages.adpecustodian.invalidaccountid","Invalid account id");
        }

        // Get an array of the alternate custodians assigned to the adpe account
        Integer[] custodianId = (Integer[]) custodianMap.get("alternateCustodianUserId");

        AdpeAccount acct = adpeAccountDao.getById(accountId);

        for (int i = 0; i < custodianId.length; i++) {
            User user = userDao.getById(custodianId[i]);

            if (acct.getPrimaryCustodian().contains(user)) {
                throw new IllegalUnassignmentOfPrimaryCustodianException();
            }
            acct.getAlternateCustodians().add(user);
            adpeAccountDao.saveOrUpdate(acct);

            StringBuilder sb = new StringBuilder();
            sb.append("You have been assigned as Alternate equipment custodian for ADPE Account ");
            sb.append(acct.getName());
            sb.append(" . If this is in error, please contact the Primary Account Custodian \r\n");
            new JavaMailSimpleMailSender().sendMessage(user,"Topic Assignment As Equipment Custodian", sb.toString());

        }


    }

    public void removeAlternateCustodian(int accountId, int userId) throws AdpeAccountNotFoundException,
            UserNotFoundException {

        AdpeAccount acct = adpeAccountDao.getById(accountId);
        if (acct == null) {
            throw new AdpeAccountNotFoundException();
        }

        User user = userDao.getById(userId);
        if (user == null) {
            throw new UserNotFoundException();
        }

        Set<User> alternateCustodianList = new TreeSet<User>(acct.getAlternateCustodians());
        alternateCustodianList.remove(user);
        acct.setAlternateCustodians(alternateCustodianList);
        //acct.getAlternateCustodians().remove(user);

        adpeAccountDao.saveOrUpdate(acct);

    }

    public void saveOrUpdate(AdpeAccount account) {

        adpeAccountDao.saveOrUpdate(account);
    }

}
